//
// Created by huyi on 24-7-12.
//
#include "QuoteAmaLiteService.h"

#include <folly/cli/NestedCommandLineApp.h>
#include <folly/dynamic.h>
#include <folly/experimental/io/FsUtil.h>
#include <folly/io/async/EventBase.h>
#include <folly/logging/FileHandlerFactory.h>
#include <folly/logging/Init.h>
#include <folly/logging/xlog.h>
#include <folly/portability/Filesystem.h>

#include <gflags/gflags.h>

#include <csignal>
#include <iostream>
#include <string>
#include <utility>

DEFINE_string(ama_server_ip1, "10.188.68.10", "AMA服务IP1");
DEFINE_string(ama_server_ip2, "10.188.68.10", "AMA服务IP2");
DEFINE_int32(ama_server_port1, 8400, "AMA服务端口1");
DEFINE_int32(ama_server_port2, 8400, "AMA服务端口2");
DEFINE_string(ama_username, "", "AMA用户名");
DEFINE_string(ama_password, "", "AMA密码");

DEFINE_string(ddb_server, "127.0.0.1", "DolphinDB 地址");
DEFINE_uint32(ddb_port, 8848, "DolphinDB 端口");
DEFINE_string(ddb_username, "admin", "DolphinDB 用户名");
DEFINE_string(ddb_password, "123456", "DolphinDB 用户名");

DEFINE_int32(print_period, 100000, "快照打印间隔(条)");
DEFINE_int32(dolphindb_check_seconds, 5, "检测dolphindb连接的间隔(秒)");
DEFINE_int32(ama_check_seconds, 5, "检测AMA连接的间隔(秒)");

DEFINE_uint32(queue_size, 1000000U, "消息队列长度");
DEFINE_int32(queue_consumer_max_read_size, 10000, "消费者每批能处理的最大条数");

// 日志配置
FOLLY_INIT_LOGGING_CONFIG(
  ".=DBG9:default:f; default=stream:stream=stdout,async=true,sync_level=WARNING; f=file:path=ama-service.log");
//FOLLY_INIT_LOGGING_CONFIG(
//  ".=DBG9,folly=INFO; default:async=true,sync_level=WARNING");

namespace tradev::app {

#if defined(_MSC_VER)
std::string guessProgramName() {
  char  path[MAX_PATH];
  DWORD result = GetModuleFileName(NULL, path, MAX_PATH);
  if (result == 0) {
    // 获取路径失败，处理错误
    return "";
  }
  return folly::fs::path(path).stem().string();
}
#else
// Guess the program name as basename(executable)
std::string guessProgramName() {
  try {
    return folly::fs::executable_path().filename().string();
  } catch (const std::exception& ex) {
    XLOG(ERR) << "Get executable path fails " << ex.what();
    return "UNKNOWN";
  }
}
#endif

folly::dynamic variablesMapToDynamic(const VMap& vm) {
  folly::dynamic config = folly::dynamic::object();

  auto addToConfig = [&config](const auto& key, const auto& value) {
    using ValueType = std::decay_t<decltype(value)>;
    if constexpr (std::is_same_v<ValueType, const char*>) {
      config[key] = std::string(value);
    } else if constexpr (std::is_same_v<ValueType, std::vector<std::string>>) {
      config[key] = folly::dynamic::array();
      for (const auto& item : value) {
        config[key].push_back(item);
      }
    } else {
      config[key] = value;
    }
  };

  for (const auto& option : vm) {
    const auto& value = option.second.value();
    if (value.type() == typeid(const char*)) {
      addToConfig(option.first, boost::any_cast<const char*>(value));
    } else if (value.type() == typeid(std::string)) {
      addToConfig(option.first, boost::any_cast<std::string>(value));
    } else if (value.type() == typeid(int)) {
      addToConfig(option.first, boost::any_cast<int>(value));
    } else if (value.type() == typeid(double)) {
      addToConfig(option.first, boost::any_cast<double>(value));
    } else if (value.type() == typeid(bool)) {
      addToConfig(option.first, boost::any_cast<bool>(value));
    } else if (value.type() == typeid(uint32_t)) {
      addToConfig(option.first, boost::any_cast<uint32_t>(value));
    } else if (value.type() == typeid(long)) {
      addToConfig(option.first, boost::any_cast<long>(value));
    } else if (value.type() == typeid(std::vector<std::string>)) {
      addToConfig(option.first, boost::any_cast<std::vector<std::string>>(value));
    } else {
      std::cerr << "Unsupported type for option: " << option.first << std::endl;
    }
  }
  return config;
}

void runStart(const VMap& options, const Args& args) {
  auto config = variablesMapToDynamic(options);

  // 主EventBase
  folly::EventBase evb;

  // 创建服务
  auto service = std::make_unique<QuoteAmaLiteService>(&evb, config, args);

// 信号处理程序必须最优先注册(特别是在日志初始化前),否则无法正确处理信号
#if defined(_MSC_VER)
  const auto SIGNALS = {SIGINT, SIGTERM};
#else
  const auto SIGNALS = {SIGINT, SIGUSR1, SIGUSR2, SIGTERM};
#endif
  QuoteAmaLiteService::SignalHandler handler{&evb, service.get(), SIGNALS};

  // 开启文件日志支持
  folly::LoggerDB::get().registerHandlerFactory(
    std::make_unique<folly::FileHandlerFactory>());
  // 初始化日志
  folly::initLoggingOrDie();

  // 初始化
  if (auto rc = service->open() != 0) {
    throw folly::ProgramExit(rc);
  }

  // 开始循环
  service->run();
}
} // namespace tradev::app

int main(int argc, char* argv[]) {
  using namespace tradev::app;
  folly::NestedCommandLineApp app("", "0.1");

  // 注释格式
  int style = boost::program_options::command_line_style::default_style;
  style &= ~boost::program_options::command_line_style::allow_guessing;
  app.setOptionStyle(static_cast<boost::program_options::command_line_style::style_t>(style));

  // 加入通过GFlags定义的全局参数,这些参数对所有Command都有效
  app.addGFlags();

  // 加载.gflags文件
  auto conf = folly::fs::absolute(folly::fs::path("./") / (guessProgramName() + ".gflags")).lexically_normal().generic_string();
  XLOG(INFO) << "Config file path: " << conf;
  if (folly::fs::exists(conf)) {
    XLOG(INFO) << "Load " << conf;
    if (google::SetCommandLineOption("flagfile", conf.c_str()).empty()) {
      return 1;
    }
  }

  // Add any commands. For our example, we'll implement simplified versions
  // of "cat" and "echo". Note that addCommand() returns a reference to a
  // boost::program_options object that you may use to add command-specific
  // options.
  // clang-format off
  app.addCommand(
    "start",
    "",
    "Start the service",
    "启动服务程序.",
    runStart);
  // clang-format on

  // app.run returns an appropriate error code
  return app.run(argc, argv);
}
